//遍历dom结构，解析插值表达式或者指令
class Compile {
    constructor(el, vm) {
        this.$vm = vm;
        this.$el = document.querySelector(el);

        // 把模板中的内容移到片段操作
        this.$fragment = this.node2Fragment(this.$el);
        // 执行编译
        this.compile(this.$fragment);
        // 放回$el中
        this.$el.appendChild(this.$fragment);
    }
    node2Fragment(el) {
        // 创建元素片段
        const fragment = document.createDocumentFragment();
        let child;
        while ((child = el.firstChild)) {
            fragment.appendChild(child);
        }
        return fragment;
    }
    compile(el) {
        const childNodes = el.childNodes;
        Array.from(childNodes).forEach(node => {
            if (node.nodeType == 1) { //表示元素节点
                 console.log(`编译${node.nodeName}元素:`)
                // // console.log(node)
                //只编译v-xxx
                this.compileElement(node);
            } else if (this.isInter(node)) { //是否为插值表达式
                // //console.log(node)
                //编译{{}}
                this.compileText(node)
            }
            // 递归子节点
            if (node.children && node.childNodes.length > 0) {
                this.compile(node);
            }
        })
    }
    isInter(node) {
        return node.nodeType == 3 && /\{\{(.*)\}\}/.test(node.textContent);
    }
    compileText(node) { //文本替换
        // 取出插值表达式内中的表达式
        const exp = RegExp.$1;
        this.update(node, exp, 'text')
    }
    update(node, exp, type) {
        const updator = this[type + "Updator"];
        updator && updator(node, this.$vm[exp]); // 首次初始化
        // 创建Watcher实例，收集依赖
        new Watcher(this.$vm, exp, function (val) {
            updator && updator(node, val);
        })
    }
    textUpdator(node, val) {
        console.log(node, val)
        //将插值表达式变为vm.$data的值
        node.textContent = val;
    }
    compileElement(node) {
        //只关心元素属性
        const nodeAttrs = node.attributes;
        console.log(node,nodeAttrs)
        Array.from(nodeAttrs).forEach(attr => {
            //v-xxx="aaa"
            const attrName = attr.name; //取出指令v-xxx
            const exp = attr.value; //取出指令值aaa
            console.log(attrName,exp)
            if (attrName.indexOf("v-") == 0) {
                // 指令
                const type = attrName.substring(2); //xxx
                // 执行
                this[type] && this[type](node, exp);
            } else if (attrName.indexOf('@') == 0) {
                //事件类型
                const type = attrName.substring(1);
                console.log(type);
                //执行
                this.handleEvent(node, this.$vm, exp, type)
            }
        })
    }
    handleEvent(node, vm, exp, type) {
        const fn = vm.$options.methods && vm.$options.methods[exp]
        if (type && fn) {
            node.addEventListener(type, fn.bind(vm))
        }
    }
    text(node, exp) { //v-text
        console.log(node, exp, 'v-text')
        this.update(node, exp, 'text')
    }
    html(node, exp) { //v-html
        this.update(node, exp, "html");
        console.log(node, exp, this.$vm[exp], 'v-html')  
    }
    htmlUpdator(node, val) {
        node.innerHTML = val;
    }
    model(node, exp) {//v-model
        this.update(node, exp, 'model')
        node.addEventListener('input', e => {
            this.$vm[exp] = e.target.value;
        })
    }
    modelUpdator(node, val) {
        node.value = val;
    }
}